var ImgTools = new function () 
{
  var processState = 0;
  var debugImage = false;
  function getHex(b)
  {
    return ('0'+(b|0).toString(16)).slice(-2)
  }
  function floatToColor(rv, gv, bv) 
  {
    return "#"+getHex(rv*255)+getHex(gv*255)+getHex(bv*255);
  }
  function linearMove(value, adjust) 
  {
    if (adjust+2.0<value)
    {
      value = value-3.0;
    }
    var center = Math.abs(value-adjust);
    return Math.max(0,1.0 - center);
  }
  function colorFromValue(lv)
  {
      return {r:linearMove(lv*3, 0), g:linearMove(lv*3, 1.0),b: linearMove(lv*3, 2.0)};
  }
  this.colorSliderElement = function(colorSlider, dst)
  {
      var c = colorFromValue(colorSlider.value/colorSlider.max);
      dst.style.backgroundColor= floatToColor(c.r,c.g,c.b);
  }
  this.imageProcess = function(imgSrc, canvas, colorSlider)
  {
    if (processState == 0)
    {
      return;
    }
    processState = 2;
    var srcCTX=imgSrc.getContext("2d");
    var imgData=srcCTX.getImageData(0,0,imgSrc.width,imgSrc.height);
    
    var ctx=canvas.getContext("2d");
    
    
    // convert to grayscale
    var pGray = this.getGrayScaled(imgData.data, imgData.width*imgData.height);
    
    // box blur
    var pBlur = pGray;
    for (var i=0; i<8; i++)
    {
      pBlur = this.getBlur(pBlur.data, imgData.width, imgData.height);
    }
    
    // create value histogram
    var pHistogram = this.getHistogram(pBlur.data, pBlur.dataSize);
    
    // merge values against histogram
    var pMergeClose = this.getMergeClose(pHistogram.data, pHistogram.dataSize,pBlur.data, pBlur.dataSize,8)
    
    if (debugImage)
    {
      var moveLeft = 0;
      canvas.width = imgSrc.width*3+256;
      canvas.height = 256;
      var pGrayRGBA = this.getRGBA(pGray.data,pGray.data,pGray.data, pGray.dataSize);
      var pBlurRGBA = this.getRGBA(pBlur.data,pBlur.data,pBlur.data, pBlur.dataSize);
      var pMergeCloseRGBA = this.getRGBA(pMergeClose.data,pMergeClose.data,pMergeClose.data, pMergeClose.dataSize);
    
      ctx.putImageData(this.createImageData(ctx, pGrayRGBA.data,imgData.width, imgData.height),moveLeft,0);
      moveLeft = moveLeft+imgData.width;
      ctx.putImageData(this.createImageData(ctx, pBlurRGBA.data,imgData.width, imgData.height),moveLeft,0);
      moveLeft = moveLeft+imgData.width;
      ctx.putImageData(this.createImageData(ctx, pMergeCloseRGBA.data,imgData.width, imgData.height),moveLeft,0);
      moveLeft = moveLeft+imgData.width;
    
      var pBars = this.getBars(pHistogram.data, pHistogram.dataSize, pHistogram.dataSize);
      var pBarsRGBA = this.getRGBA(pBars.data,pBars.data,pBars.data, pBars.dataSize);
      ctx.putImageData(this.createImageData(ctx, pBarsRGBA.data,pHistogram.dataSize,pHistogram.dataSize),moveLeft,0);
    }
    else
    {
      canvas.width = imgSrc.width;
      canvas.height = imgSrc.height;
      
      var c = colorFromValue(colorSlider.value/colorSlider.max);
        
      var pNormalized = this.normalize(pMergeClose.data, pMergeClose.dataSize);
     
      var pMergeCloseRGBA = this.grayToColor(pNormalized.data, pNormalized.dataSize, c);
      
      ctx.putImageData(this.createImageData(ctx, pMergeCloseRGBA.data,imgData.width, imgData.height),0,0);
    }
  }
  this.normalize = function(data, dataSize)
  {
    
    var max = this.getMax(data, dataSize);
    var min = this.getMin(data, dataSize);
    if (min.value == max.value)
    {
      return {data:data, dataSize:dataSize};
    }
    var newData = {data:[], dataSize:dataSize};
    var size = max.value-min.value;
    for (var i=0; i<dataSize; i++)
    {
      newData.data[i] = Math.floor(((data[i]-min.value)*1.0/size)*255);
    }
    return newData;
  }
  this.getBlur = function(data, width, height)
  {
    var newData = {data:[], dataSize:width*height};
    for (var y=0; y<height; y++)
    {
      for (var x=0; x<width; x++)
      {
        var xl = Math.max(0,x-1);
        var xr = Math.min(width-1,x+1);
        var yl = Math.max(0,y-1);
        var yr = Math.min(height-1,y+1);
        
        var index = y*width+x;
        
        newData.data[index] = Math.floor((
          data[index] + 
          data[y*width+xl] +
          data[y*width+xr] +
          data[yl*width+x] +
          data[yr*width+x] )/5);
      }
    }
    return newData;
  }
  this.getSmooth = function(data, dataSize)
  {
    var newData = {data:[], dataSize:dataSize};
    for (var i=0; i<dataSize; i++)
    {
      var ib = Math.max(0, i-1);
      var ia = Math.min(dataSize-1, i+1);
      newData.data[i] = (data[ib] + data[i] + data[ia])/3;
    }
    return newData;
  }
  this.getMergeClose = function(hData, hDataSize, dData, dDataSize, num)
  {
    var newData = {data:[], dataSize:dDataSize};
    for (var i=0; i<dDataSize; i++)
    {
      newData.data[i] = 255;
    }
    
    var maxCount = dDataSize/num;
    var currCount = 0;
    var value = 0;
    var startIndex = 0;
    for (var i=0; i<hDataSize+1; i++)
    {
      if (i==hDataSize) // last
      {
        maxCount = 0;
      }
      else
      {
        currCount = currCount + hData[i];
        value = value + i*hData[i];
      }
      if (currCount > maxCount)
      {
        var newVal = Math.floor(value/currCount);
        for (var j=0; j<dDataSize; j++)
        {
          if (dData[j] >= startIndex && dData[j] <= i)
          {
            newData.data[j] = newVal;
          }
        }
        currCount = 0;
        value = 0;
        startIndex = i+1;
      }
    }
    return newData;
  }
  this.getRGBA = function(red,green,blue, dataSize)
  {
    var newData = {data:[], dataSize:dataSize*4};
    for (var i=0; i<dataSize; i++)
    {
      newData.data[i*4] = red[i];
      newData.data[i*4+1] = green[i];
      newData.data[i*4+2] = blue[i];
      newData.data[i*4+3] = 255;
    }
    return newData;
  }
  this.storeImage = function(imgSrc)
  {
    if (processState != 2)
    {
      return;
    }
    processState = 1;
    var index = 0;
    if (localStorage.imgIndex && localStorage.imgIndex < 4)
    {
      index = parseInt(localStorage.imgIndex)+1;
    }
    localStorage.setItem("img"+index, imgSrc.toDataURL());
    localStorage.imgIndex = index;
  }
  this.getImages = function()
  {
    var images = [];
    for (var i=0; i<5; i++)
    {
      var value = localStorage.getItem("img"+i);
      if (value)
      {
        var img = new Image;
        img.src = value;
        images[images.length] = img;
      }
    }
    return images;
  }
  this.loadImages = function(canvas)
  {
    var ctx = canvas.getContext("2d");
    var images = this.getImages();
    var width = 0;
    for (var i=0; i<images.length; i++)
    {
      ctx.drawImage(images[i], width, 0);
      width = width + images[i].width;
    }
  }
  this.grayToColor = function(gray, dataSize,color )
  {
    var newData = {data:[], dataSize:dataSize*4};
    for (var i=0; i<dataSize; i++)
    {
      var value = (gray[i]/255);
      newData.data[i*4] = Math.floor(gray[i]*Math.min(1, color.r+value));
      newData.data[i*4+1] = Math.floor(gray[i]*Math.min(1, color.g+value));
      newData.data[i*4+2] = Math.floor(gray[i]*Math.min(1, color.b+value));
      newData.data[i*4+3] = 255;
    }
    return newData;
  }
  this.doUpload  = function(input, imgSrc) 
  {
    if (input.files && input.files[0]) {
        var reader = new FileReader();
        
        reader.addEventListener("load" , function (e) {
            
            var ctx=imgSrc.getContext("2d");
            var img=new Image();
            img.src=reader.result;
            ctx.drawImage(img,0,0, ctx.canvas.width, ctx.canvas.height);
            processState = 1;
        });
        
        reader.readAsDataURL(input.files[0]);
    }
  }
  this.createImageData = function(ctx, data, width, height)
  {
    var imgdata = ctx.createImageData(width, height);
    for (var i = 0; i < width*height*4; i++) 
    {
        imgdata.data[i] = data[i];
    }
    return imgdata;
  }
  this.getHistogram = function(data, dataSize)
  {
    var newData = {data:[], dataSize:256};
    for (var i=0; i<256; i++)
    {
      newData.data[i] = 0;
    }
    for (var i=0; i<dataSize; i++)
    {
      var index = Math.max(0,Math.min(255, data[i]));
      newData.data[index] = newData.data[index]+1;
    }
    return newData;
  }
  
  this.getMin = function(data, dataSize)
  {
    var minValue = {value:10000000,index:-1};
    for (var i=0; i<dataSize; i++)
    {
      if (data[i] < minValue.value)
      {
        minValue.value = data[i];
        minValue.index = i;
      }
    }
    return minValue;
  }
  this.getMax = function(data, dataSize)
  {
    var maxValue = {value:0,index:-1};
    for (var i=0; i<dataSize; i++)
    {
      if (data[i] > maxValue.value)
      {
        maxValue.value = data[i];
        maxValue.index = i;
      }
    }
    return maxValue;
  }
  this.getBars = function(data, dataSize, height)
  {
    var width = dataSize;
    var newData = {data:[], dataSize:width*height};
    var maxValue = this.getMax(data, dataSize);
    for (var x=0; x<dataSize; x++)
    {
      var clipVal = data[x]*height/maxValue.value;
      for (var y=0; y<height; y++)
      {
        var i=(height-y-1)*width+x;
        if (y<clipVal)
        {
          newData.data[i] = 0;
        }
        else
        {
          newData.data[i] = 255;
        }
      }   
    }
    return newData;
  }
  this.getBorderColor = function(data, width, height)
  {
    var newData = { data:[], dataSize:((height-2)*2+(width-2)*2)};
    
    var i=0;
    for (var x=0; x<width; x++)
    {
      newData.data[i] = data[x];
      i++;
    }
    for (var y=1; y<height-1; y++)
    {
      var yr = (width*y)-1;
      newData.data[i] = data[yr];
      i++;
    }
    for (var x=0; x<width; x++)
    {
      var xb =  (width-x-1)+((height-1)*width);
      newData.data[i] = data[xb];
      i++;
    }
    for (var y=1; y<height-1; y++)
    {
      var yr = width*(height-y-1);
      newData.data[i] = data[yr];
      i++;
    }
    return newData;
  }
  this.getGrayScaled = function(data, dataSize)
  {
    var newData = {data:[], dataSize: dataSize};
    for (var i=0; i<dataSize; i++)
    {
      var gray = Math.floor(Math.min(255, data[i*4]*0.222 + data[i*4+1]*0.707 + data[i*4+2]*0.071));
      newData.data[i] = gray;
    }
    return newData;
  }
}